package com.framework.auth.interceptor;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.framework.auth.config.AuthConfig;
import com.framework.auth.constants.AuthConstants;
import com.framework.auth.context.LoginContext;
import com.framework.auth.exception.UnauthorizedException;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

@Component
@Order(1)  // 确保拦截器优先级最高
public class AuthInterceptor implements HandlerInterceptor {

   
    private final AuthConfig authConfig;
    private final OkHttpClient client;
    private final ObjectMapper objectMapper;  // 声明 Gson 成员变量
    private final ApplicationContext applicationContext;
    // 常量配置
    public static final String CODE_KEY = "code";
    public static final String BODY_KEY = "body";
    public static final String PATTERN = "(\\.[a-zA-Z0-9]+)?";
    // 用于缓存预编译的正则表达式
    private final List<Pattern> authPathPatterns = new ArrayList<>();
    private static final Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);
    @Autowired
    public AuthInterceptor(AuthConfig authConfig, ApplicationContext applicationContext) {
        this.authConfig = authConfig;
        this.applicationContext = applicationContext;
        String loginType = authConfig.getLoginType();
        // 初始化 OkHttpClient，使用配置文件中的参数
        this.client =  AuthConstants.LOGIN_TYPE_HTTP.equals(loginType)?null:new OkHttpClient.Builder()
                .connectionPool(new okhttp3.ConnectionPool(authConfig.getMaxConnections(), authConfig.getKeepAliveDuration(), java.util.concurrent.TimeUnit.MINUTES))
                .connectTimeout(authConfig.getConnectTimeout(), java.util.concurrent.TimeUnit.SECONDS)
                .readTimeout(authConfig.getReadTimeout(), java.util.concurrent.TimeUnit.SECONDS)
                .writeTimeout(authConfig.getWriteTimeout(), java.util.concurrent.TimeUnit.SECONDS)
                .retryOnConnectionFailure(authConfig.isRetryOnConnectionFailure())
                .connectionPool(new okhttp3.ConnectionPool(authConfig.getMaxConnections(), authConfig.getKeepAliveDuration(), java.util.concurrent.TimeUnit.SECONDS))
                .build();

        this.objectMapper = new ObjectMapper();

        // 预编译路径配置中的正则表达式并缓存
        for (String path : authConfig.getAuthPaths()) {
            if (path.endsWith("/*")) {
                // 如果路径配置以 /* 结尾，表示匹配该路径下的子路径
                String regexPath = path.substring(0, path.length() - 2) + ".*"; // 去掉 /*，并替换为 .*
                authPathPatterns.add(Pattern.compile(regexPath));
            } else {
                // 如果路径配置没有 /*，表示只匹配精确路径
                String regexPath = path + PATTERN; // 支持路径后缀，如 .do 等
                authPathPatterns.add(Pattern.compile(regexPath));
            }
        }
    }

    @Override
    public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, Object handler) throws Exception {
        try {
            String requestPath = request.getRequestURI();
            String cleanedRequestPath = removeExtension(requestPath);  // 去掉请求路径中的后缀部分

            // 如果路径匹配需要鉴权的规则，则执行鉴权逻辑
            if (isPathProtected(requestPath)) {
                String token = request.getHeader("token");
                authenticateRequest(token);
            }
            return true;  // 匹配成功或没有需要鉴权的路径，继续执行后续逻辑
        } catch (Exception e) {
            logger.debug("AuthInterceptor preHandle error", e);
            throw new UnauthorizedException("未登录或登录过期");
        }
    }

    /**
     * 去掉路径的后缀部分，如 .do、.html 等
     */
    private String removeExtension(String path) {
        int index = path.lastIndexOf('.');
        if (index > 0) {
            return path.substring(0, index);  // 去掉后缀
        }
        return path;
    }

    /**
     * 判断请求路径是否匹配需要鉴权的路径
     */
    private boolean isPathProtected(String requestPath) {
        for (Pattern pattern : authPathPatterns) {
            if (pattern.matcher(requestPath).matches()) {
                logger.info("requestPath：{} matcher success",requestPath);
                return true;
            }
        }
        return false;
    }

    /**
     * 执行认证逻辑
     */
    private void authenticateRequest(String token) throws Exception {
        if (token == null || token.isEmpty()) {
            logger.info("authenticateRequest token：{} is null",token);
            throw new UnauthorizedException("未登录或登录过期");
        }

        if (AuthConstants.LOGIN_TYPE_HTTP.equals(authConfig.getLoginType())) {
            authenticateByHttp(token);
        } else {
            authenticateLocally(token);
        }
    }

    private void authenticateByHttp(String token) throws IOException {

        String responseBody = sendPostRequest(token);  // 发送 POST 请求并获取响应体
        boolean isAuthenticated = parseAuthenticationResponse(responseBody);  // 解析响应并判断认证状态

        if (!isAuthenticated) {
            throw new UnauthorizedException("未登录或登录过期");
        }
    }

    // 发送 POST 请求并返回响应体
    private String sendPostRequest(String token) throws IOException {
        // 使用表单提交数据格式
        FormBody formBody = new FormBody.Builder()
                .add(AuthConstants.TOKEN_HEADER, token)  // 将 token 添加为表单字段
                .build();

        Request postRequest = new Request.Builder()
                .url(authConfig.getUserServiceUrl())
                .post(formBody)
                .build();

        try (Response response = client.newCall(postRequest).execute()) {
            if (!response.isSuccessful()) {
                throw new UnauthorizedException("登录信息判断异常");
            }
            assert response.body() != null;
            return response.body().string();  // 返回响应体
        }
    }

    private boolean parseAuthenticationResponse(String responseBody) {
        TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
        Map<String, Object> responseMap = null;
        try {
            responseMap = objectMapper.readValue(responseBody, typeRef);
            installHeaderThreadLocal(responseMap);
            // 可以直接访问 responseMap 里的属性
        } catch (JsonProcessingException e) {
            logger.error("数据解析异常:{}", e.getMessage(), e);
            throw new RuntimeException(e);
        }
        return responseMap != null;
    }

    private void authenticateLocally(String token) throws Exception {
        // 获取本地服务的类名，并通过 Spring Context 获取对应的 Bean
        String localServiceClassName = authConfig.getLocalServiceClass();
        Class<?> localServiceClass = Class.forName(localServiceClassName);
        Object localServiceBean = applicationContext.getBean(localServiceClass);

        // 假设 authenticate 方法存在并且接受一个 token 参数
        Method authenticateMethod = localServiceClass.getMethod("authenticate2", String.class);
        Object invoke = authenticateMethod.invoke(localServiceBean, token);
        if (invoke ==null) {
            throw new UnauthorizedException("未登录或登录过期");
        }
        Map<String,Object> map = objectMapper.convertValue(invoke, Map.class);

        installHeaderThreadLocal(map);
    }

    private static void installHeaderThreadLocal(Map<String, Object> map) {
        if (map == null || map.isEmpty()) {
            logger.error("Input map is null or empty.");
            throw new UnauthorizedException("未登录或登录过期");
        }

        String code = (String) map.get(CODE_KEY);

        // 如果code不为200，直接抛出异常
        if (!AuthConstants.CODE_SUCCESS.equals(code)) {
            logger.warn("Login failed, code: {}", code);
            throw new UnauthorizedException("未登录或登录过期");
        }

        // 从map中获取body并进行校验
        Object bodyObj = map.get(BODY_KEY);
        if (!(bodyObj instanceof Map)) {
            logger.error("Invalid body, expected a Map, but found: {}", bodyObj == null ? "null" : bodyObj.getClass());
            throw new UnauthorizedException("未登录或登录过期");
        }

        Map<String, Object> body = (Map<String, Object>) bodyObj;

        // 将body存放到ThreadLocal中
        LoginContext.setLoginInfo(body);
        logger.info("Login successful, user info stored in ThreadLocal. Body: {}", body);

        // 日志打印整个map内容，便于追踪
        logger.info("map content: {}", map);
    }


    private void processAuthResponse(String responseBody) throws JsonProcessingException {
        // 解析 HTTP 返回的 JSON 数据
        Map<String, Object> responseMap = objectMapper.readValue(responseBody, new TypeReference<Map<String, Object>>() {});

        installHeaderThreadLocal(responseMap);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 请求处理完成后清除 ThreadLocal 中存储的登录信息
        LoginContext.clear();
        logger.info("ThreadLocal cleared after request completion.");
    }
}
